home *** CD-ROM | disk | FTP | other *** search
/ Aminet 37 / Aminet 37 (2000)(Schatztruhe)[!][Jun 2000].iso / Aminet / dev / lang / sofa.lha / sofa / smalleiffel / lib_std / dictionary.e < prev    next >
Text File  |  2000-03-25  |  20KB  |  700 lines

  1. -- This file is  free  software, which  comes  along  with  SmallEiffel. This
  2. -- software  is  distributed  in the hope that it will be useful, but WITHOUT 
  3. -- ANY  WARRANTY;  without  even  the  implied warranty of MERCHANTABILITY or
  4. -- FITNESS  FOR A PARTICULAR PURPOSE. You can modify it as you want, provided
  5. -- this header is kept unaltered, and a notification of the changes is added.
  6. -- You  are  allowed  to  redistribute  it and sell it, alone or as a part of 
  7. -- another product.
  8. --          Copyright (C) 1994-98 LORIA - UHP - CRIN - INRIA - FRANCE
  9. --            Dominique COLNET and Suzanne COLLIN - colnet@loria.fr 
  10. --                       http://SmallEiffel.loria.fr
  11. --
  12. class DICTIONARY[V,K->HASHABLE]
  13.    --
  14.    -- Associative memory. 
  15.    -- Values of type `V' are stored using Keys of type `K'.
  16.    --
  17.  
  18. inherit ANY redefine is_equal, copy end;
  19.  
  20. creation make, with_capacity
  21.  
  22. feature 
  23.  
  24.    Default_size: INTEGER is 32;
  25.          -- Minimum size for storage in muber of items.
  26.  
  27. feature {DICTIONARY}
  28.    
  29.    keys: FIXED_ARRAY[K];
  30.          -- Storage for keys of type `K'.
  31.  
  32.    store: FIXED_ARRAY[V];
  33.          -- Storage for values of type `V'.
  34.     
  35.    modulus: INTEGER;
  36.          -- To compute a hash value in range [0 .. `modulus'-1].
  37.  
  38.    buckets: FIXED_ARRAY[INTEGER];
  39.          -- Valid index range is always [0 .. `modulus'-1].
  40.          -- Contents is a hash code value in range [0 .. `keys.upper'] to 
  41.          -- acess `keys', `store' and `chain' as well.
  42.     
  43.    chain: FIXED_ARRAY[INTEGER]; 
  44.          -- Used to chain both free slots and hash-code clash.
  45.          -- Value -1 mark the end of a list.
  46.     
  47.    first_free_slot: INTEGER;
  48.          -- First element of the free-slot list or -1 when no more
  49.          -- free slot are available.
  50.  
  51. feature {DICTIONARY}  -- Internal cache handling :
  52.     
  53.    cache_keys_idx: INTEGER;
  54.          -- Contents is -1 or caches the last visited entry using : `has',
  55.          -- `at', `item', or `key'.
  56.  
  57.    cache_user_idx: INTEGER;
  58.          -- Contents is -1 or in range [1 .. `count']. When not -1, it 
  59.          -- caches the last user's index used with `item' or `key'.
  60.  
  61.    cache_buckets_idx: INTEGER;
  62.          -- Contents means nothing when `cache_user_idx' is -1.
  63.          -- Otherwise, gives the current position in `buckets' during 
  64.          -- traversal.
  65.  
  66. feature {NONE}
  67.  
  68.    buckets_keys_ratio: INTEGER is 3;
  69.          -- To compute `modulus' as `ratio' * `capacity'.
  70.  
  71.    make is
  72.          -- Internal initial storage size of the dictionary is set to
  73.          -- the default `Default_size' value. Then, tuning of needed storage 
  74.          -- size is done automatically according to usage. 
  75.          -- If you are really sure that your dictionary is always really
  76.          -- bigger than `Default_size', you may use `with_capacity' to save some 
  77.          -- execution time.
  78.       do
  79.          with_capacity(Default_size);
  80.       ensure
  81.          is_empty;
  82.          capacity = Default_size
  83.       end;
  84.    
  85.    with_capacity(medium_size: INTEGER) is
  86.          -- May be used to save some execution time if one is sure 
  87.          -- that storage size will rapidly become really bigger than
  88.          -- `Default_size'. When first `remove' occurs, storage size may 
  89.          -- naturally become smaller than `medium_size'. Afterall, 
  90.          -- tuning of storage size is done automatically according to
  91.          -- usage.
  92.       require
  93.          medium_size > 0
  94.       local
  95.          i: INTEGER;
  96.       do
  97.          !!keys.make(medium_size);
  98.          !!store.make(medium_size);
  99.          modulus := buckets_keys_ratio * medium_size;
  100.          !!buckets.make(modulus);
  101.          buckets.set_all_with(-1);
  102.          from
  103.             !!chain.make(medium_size);
  104.             i := chain.upper;
  105.             first_free_slot := i;
  106.          until
  107.             i < 0
  108.          loop
  109.             chain.put(i - 1, i);
  110.             i := i - 1;
  111.          end;
  112.          cache_keys_idx := -1;
  113.          cache_user_idx := -1;
  114.          count := 0;
  115.       ensure
  116.          is_empty;
  117.          capacity = medium_size
  118.       end;
  119.  
  120. feature -- Counting :
  121.  
  122.    count: INTEGER;
  123.          -- Actual `count' of stored elements.
  124.  
  125.    is_empty: BOOLEAN is
  126.      -- Is it empty ?
  127.       do
  128.          Result := count = 0;
  129.       ensure
  130.          Result = (count = 0)
  131.       end;
  132.       
  133.    empty: BOOLEAN is
  134.       obsolete "The new name of this feature is `is_empty'."
  135.       do
  136.      Result := is_empty;
  137.       end;
  138.       
  139. feature -- Basic access :
  140.  
  141.    has(k: K): BOOLEAN is
  142.          -- Is there an item currently associated with key `k' ?
  143.       do
  144.          if cache_keys_idx < 0 or else k /= keys.item(cache_keys_idx) then
  145.             from
  146.                cache_user_idx := -1;
  147.                cache_keys_idx := buckets.item(k.hash_code \\ modulus);
  148.             until
  149.                cache_keys_idx < 0 or else
  150.                k.is_equal(keys.item(cache_keys_idx))
  151.             loop
  152.                cache_keys_idx := chain.item(cache_keys_idx);
  153.             end;
  154.          end;
  155.          Result := (cache_keys_idx >= 0);
  156.       end;
  157.    
  158.    at, infix "@" (k: K): V is
  159.          -- Return the item stored at key `k'.
  160.       require
  161.          has(k)
  162.       do
  163.          if cache_keys_idx < 0 or else k /= keys.item(cache_keys_idx) then
  164.             from
  165.                cache_user_idx := -1;
  166.                cache_keys_idx := buckets.item(k.hash_code \\ modulus);
  167.             until
  168.                k.is_equal(keys.item(cache_keys_idx))
  169.             loop
  170.                cache_keys_idx := chain.item(cache_keys_idx);
  171.             end;
  172.          end;
  173.          Result := store.item(cache_keys_idx);
  174.       end;
  175.    
  176. feature -- The only way to add or to change an entry :
  177.  
  178.    put(v: V; k: K) is
  179.          -- If there is as yet no key `k' in the dictionary, enter 
  180.          -- it with item `v'. Otherwise overwrite the item associated
  181.          -- with key `k'.
  182.       local
  183.          h: INTEGER;
  184.       do
  185.          if cache_keys_idx < 0 or else k /= keys.item(cache_keys_idx) then
  186.             from
  187.                cache_user_idx := -1;
  188.                h := k.hash_code \\ modulus;
  189.                cache_keys_idx := buckets.item(h);
  190.             until
  191.                cache_keys_idx < 0 or else 
  192.                k.is_equal (keys.item (cache_keys_idx))
  193.             loop
  194.                cache_keys_idx := chain.item (cache_keys_idx);
  195.             end;
  196.             if cache_keys_idx < 0 then
  197.                if first_free_slot < 0 then
  198.                   expand;
  199.                   h := k.hash_code \\ modulus;
  200.                end;
  201.                keys.put(k,first_free_slot);
  202.                store.put(v,first_free_slot);
  203.                cache_keys_idx := first_free_slot;
  204.                first_free_slot := chain.item(first_free_slot);
  205.                chain.put(buckets.item(h),cache_keys_idx);
  206.                buckets.put(cache_keys_idx,h);
  207.                count := count + 1;
  208.             else
  209.                store.put(v,cache_keys_idx);
  210.             end;
  211.          else
  212.             store.put(v,cache_keys_idx);
  213.          end;
  214.       ensure
  215.          v = at(k)
  216.       end;
  217.  
  218. feature -- Looking and Searching :
  219.  
  220.    nb_occurrences(v: V): INTEGER is
  221.          -- Number of occurrences using `equal'.
  222.          -- See also `fast_nb_occurrences' to chose
  223.          -- the apropriate one.
  224.       local
  225.          i: INTEGER;
  226.       do
  227.          from
  228.             i := 1
  229.          until
  230.             i > count
  231.          loop
  232.             if equal_like(v,item(i)) then
  233.                Result := Result + 1;
  234.             end;
  235.             i := i + 1;
  236.          end;
  237.       ensure
  238.          Result >= 0
  239.       end;
  240.       
  241.    fast_nb_occurrences(v: V): INTEGER is
  242.          -- Number of occurrences using `='.
  243.       local
  244.          i: INTEGER;
  245.       do
  246.          from
  247.             i := 1
  248.          until
  249.             i > count
  250.          loop
  251.             if v = item(i) then
  252.                Result := Result + 1;
  253.             end;
  254.             i := i + 1;
  255.          end;
  256.       ensure
  257.          Result >= 0;
  258.       end;
  259.  
  260.    key_at(v: V): K is
  261.          -- Retrieve the key used for value `v' using `equal' for comparison. 
  262.       require
  263.          nb_occurrences(v) = 1
  264.       local
  265.          i: INTEGER;
  266.       do
  267.          from  
  268.             i := 1;
  269.          until
  270.             equal_like(v,item(i))
  271.          loop
  272.             i := i + 1;
  273.          end;
  274.          Result := keys.item(cache_keys_idx);
  275.       ensure
  276.          equal(at(Result),v)
  277.       end;
  278.    
  279.    fast_key_at(v: V): K is
  280.          -- Retrieve the key used for value `v' using `=' for comparison. 
  281.       require
  282.          fast_nb_occurrences(v) = 1
  283.       local
  284.          i: INTEGER;
  285.       do
  286.          from  
  287.             i := 1;
  288.          until
  289.             v = item(i)
  290.          loop
  291.             i := i + 1;
  292.          end;
  293.          Result := keys.item(cache_keys_idx);
  294.       ensure
  295.          at(Result) = v
  296.       end;
  297.  
  298.    capacity: INTEGER is
  299.       do
  300.          Result := keys.count;
  301.       end;
  302.  
  303. feature -- Removing :
  304.  
  305.    remove(k: K) is
  306.          -- Remove entry `k' (which may exist or not before this call).
  307.       local
  308.          h, keys_idx, keys_next_idx: INTEGER;
  309.       do
  310.          h := k.hash_code \\ modulus;
  311.          keys_idx := buckets.item(h);
  312.          if keys_idx < 0 then
  313.          elseif keys.item(keys_idx).is_equal(k) then
  314.             buckets.put(chain.item(keys_idx),h);
  315.             chain.put(first_free_slot,keys_idx);
  316.             first_free_slot := keys_idx;
  317.             cache_user_idx := -1;
  318.             cache_keys_idx := -1;
  319.             count := count - 1;
  320.          else
  321.             from
  322.                keys_next_idx := chain.item(keys_idx);
  323.             until
  324.                keys_next_idx < 0 or else
  325.                keys.item(keys_next_idx).is_equal(k)
  326.                loop
  327.                   keys_idx := keys_next_idx;
  328.                   keys_next_idx := chain.item(keys_next_idx);
  329.                end;
  330.                if keys_next_idx >= 0 then
  331.                   chain.put(chain.item(keys_next_idx),keys_idx);
  332.                   chain.put(first_free_slot,keys_next_idx);
  333.                   first_free_slot := keys_next_idx;
  334.                   cache_user_idx := -1;
  335.                   cache_keys_idx := -1;
  336.                   count := count - 1;
  337.                end;
  338.             end;
  339.       ensure
  340.          not has(k)
  341.       end;
  342.  
  343.    clear is
  344.          -- Discard all items.
  345.       local
  346.          i: INTEGER;
  347.       do
  348.          buckets.set_all_with(-1);
  349.          from
  350.             i := chain.upper;
  351.             first_free_slot := i;
  352.          until
  353.             i < 0
  354.          loop
  355.             chain.put(i - 1, i);
  356.             i := i - 1;
  357.          end;
  358.          cache_keys_idx := -1;
  359.          cache_user_idx := -1;
  360.          count := 0;
  361.       ensure
  362.          is_empty;
  363.       end;
  364.  
  365. feature -- To provide iterating facilities :
  366.  
  367.    lower: INTEGER is 1;
  368.  
  369.    upper: INTEGER is
  370.       do
  371.          Result := count;
  372.       ensure
  373.          Result = count
  374.       end;
  375.  
  376.    valid_index(idx: INTEGER): BOOLEAN is
  377.       do
  378.          Result := (1 <= idx) and then (idx <= count);
  379.       ensure
  380.          Result =  (1 <= idx and then idx <= count);
  381.       end;
  382.    
  383.    item(idx: INTEGER): V is
  384.       require
  385.          valid_index(idx)
  386.       do
  387.          set_cache_user_idx(idx);
  388.          Result := store.item(cache_keys_idx);
  389.       ensure
  390.          Result = at(key(idx))
  391.       end;
  392.    
  393.    key(idx: INTEGER): K is
  394.       require
  395.          valid_index(idx)
  396.       do
  397.          set_cache_user_idx(idx);
  398.          Result := keys.item(cache_keys_idx);
  399.       ensure
  400.          at(Result) = item(idx)
  401.       end;
  402.  
  403.    get_new_iterator_on_items: ITERATOR[V] is
  404.       do
  405.          !ITERATOR_ON_DICTIONARY_ITEMS[V]!Result.make(Current);
  406.       end;
  407.  
  408.    get_new_iterator_on_keys: ITERATOR[K] is
  409.       do
  410.          !ITERATOR_ON_DICTIONARY_KEYS[K]!Result.make(Current);
  411.       end;
  412.  
  413. feature
  414.    
  415.    is_equal(other: like current): BOOLEAN is
  416.      -- Do both dictionaries have the same set of associations?
  417.      -- Keys are compared with `is_equal' and values are comnpared
  418.      -- with the basic = operator.
  419.      -- See also `is_equal_map'.
  420.       local
  421.          buckets_idx, keys_idx: INTEGER;
  422.          k: K;
  423.          v1, v2: V;
  424.       do
  425.          if Current = other then
  426.             Result := true;
  427.          elseif count = other.count then
  428.             from
  429.                Result := true;
  430.                buckets_idx := 0;
  431.             until
  432.                not Result or else buckets_idx > buckets.upper
  433.             loop
  434.                keys_idx := buckets.item(buckets_idx); 
  435.                if keys_idx >= 0 then
  436.                   from
  437.                   until
  438.                      not Result or else keys_idx < 0
  439.                   loop
  440.                      k := keys.item(keys_idx);
  441.                      if other.has(k) then
  442.                         v1 := store.item(keys_idx);
  443.                         v2 := other.at(k);
  444.                         Result := v1 = v2;
  445.                      else
  446.                         Result := false;
  447.                      end;
  448.                      keys_idx := chain.item(keys_idx);
  449.                   end;
  450.                end;
  451.                buckets_idx := buckets_idx + 1;
  452.             end;
  453.          end;
  454.       ensure
  455.      Result implies count = other.count
  456.       end;
  457.  
  458.    is_equal_map(other: like current): BOOLEAN is
  459.      -- Do both dictionaries have the same set of associations?
  460.      -- Both keys and values are compared with `is_equal'.
  461.      -- See also `is_equal'.
  462.       local
  463.          buckets_idx, keys_idx: INTEGER;
  464.          k: K;
  465.          v1, v2: V;
  466.       do
  467.          if Current = other then
  468.             Result := true;
  469.          elseif count = other.count then
  470.             from
  471.                Result := true;
  472.                buckets_idx := 0;
  473.             until
  474.                not Result or else buckets_idx > buckets.upper
  475.             loop
  476.                keys_idx := buckets.item(buckets_idx); 
  477.                if keys_idx >= 0 then
  478.                   from
  479.                   until
  480.                      not Result or else keys_idx < 0
  481.                   loop
  482.                      k := keys.item(keys_idx);
  483.                      if other.has(k) then
  484.                         v1 := store.item(keys_idx);
  485.                         v2 := other.at(k);
  486.                         Result := equal_like(v1,v2);
  487.                      else
  488.                         Result := false;
  489.                      end;
  490.                      keys_idx := chain.item(keys_idx);
  491.                   end;
  492.                end;
  493.                buckets_idx := buckets_idx + 1;
  494.             end;
  495.          end;
  496.       end;
  497.  
  498.    copy(other: like current) is
  499.      -- Reinitialize by copying all associations of `other'.
  500.       do
  501.          count := other.count;
  502.          modulus := other.modulus;
  503.          first_free_slot := other.first_free_slot;
  504.          cache_keys_idx := other.cache_keys_idx;
  505.          cache_user_idx := other.cache_user_idx;
  506.          cache_buckets_idx := other.cache_buckets_idx;
  507.          if buckets = Void then
  508.             buckets := other.buckets.twin;
  509.             keys := other.keys.twin;
  510.             store := other.store.twin;
  511.             chain := other.chain.twin;
  512.          else
  513.             buckets.copy(other.buckets);
  514.             keys.copy(other.keys);
  515.             store.copy(other.store);
  516.             chain.copy(other.chain);
  517.          end;
  518.       end;
  519.  
  520. feature {NONE} 
  521.    
  522.    expand is
  523.          -- The dictionary must grow.
  524.       local
  525.          i: INTEGER;
  526.       do
  527.          from
  528.             i := keys.count;
  529.             resize_buckets(i * 2 * buckets_keys_ratio);
  530.          until
  531.             i = 0
  532.          loop
  533.             chain.add_last(first_free_slot);
  534.             first_free_slot := chain.upper;
  535.             i := i - 1;
  536.          end;
  537.          keys.resize(chain.count);
  538.          store.resize(chain.count);
  539.       ensure
  540.          first_free_slot >= 0
  541.       end;
  542.  
  543.    resize_buckets(new_modulus: INTEGER) is
  544.       local
  545.          h, i: INTEGER;
  546.       do
  547.          modulus := new_modulus;
  548.          buckets.resize(new_modulus);
  549.          buckets.set_all_with(-1);
  550.          from
  551.          until
  552.             first_free_slot < 0
  553.          loop
  554.             i := chain.item(first_free_slot);
  555.             chain.put(-2,first_free_slot);
  556.             first_free_slot := i;
  557.          end;
  558.          check 
  559.             first_free_slot = -1;
  560.          end;
  561.          from
  562.             i := chain.upper;
  563.          until
  564.             i < 0
  565.          loop
  566.             if chain.item(i) = -2 then
  567.                chain.put(first_free_slot,i);
  568.                first_free_slot := i;
  569.             else
  570.                h := keys.item(i).hash_code \\ new_modulus;
  571.                chain.put(buckets.item(h),i);
  572.                buckets.put(i,h);
  573.             end;
  574.             i := i - 1;
  575.          end;
  576.       end;
  577.  
  578.    equal_like(v1, v2: V): BOOLEAN is
  579.          -- Note: to avoid calling `equal' :-(
  580.          -- Because SmallEiffel is not yet able to infer 
  581.          -- arguments types.
  582.       do
  583.          if v1.is_expanded_type then
  584.             Result := v1 = v2 or else v1.is_equal(v2);
  585.          elseif v1 = v2 then
  586.             Result := true;
  587.          elseif v1 = Void or else v2 = Void then
  588.          else
  589.             Result := v1.is_equal(v2);
  590.          end;
  591.       end;
  592.  
  593.    set_cache_user_idx(idx: INTEGER) is
  594.       require
  595.          valid_index(idx)
  596.       local
  597.          i: INTEGER;
  598.       do
  599.          if idx = cache_user_idx + 1 then
  600.             cache_user_idx := idx;
  601.             if chain.item(cache_keys_idx) < 0 then
  602.                from
  603.                   cache_buckets_idx := cache_buckets_idx + 1;
  604.                until
  605.                   buckets.item(cache_buckets_idx) >= 0
  606.                loop
  607.                   cache_buckets_idx := cache_buckets_idx + 1;
  608.                end;
  609.                cache_keys_idx := buckets.item(cache_buckets_idx);
  610.             else
  611.                cache_keys_idx := chain.item(cache_keys_idx);
  612.             end;
  613.          elseif idx = cache_user_idx - 1 then
  614.             cache_user_idx := idx;
  615.             if cache_keys_idx = buckets.item(cache_buckets_idx) then
  616.                from
  617.                   cache_buckets_idx := cache_buckets_idx - 1;
  618.                until
  619.                   buckets.item(cache_buckets_idx) >= 0
  620.                loop
  621.                   cache_buckets_idx := cache_buckets_idx - 1;
  622.                end;
  623.                from
  624.                   cache_keys_idx := buckets.item(cache_buckets_idx);
  625.                until
  626.                   chain.item(cache_keys_idx) < 0
  627.                loop
  628.                   cache_keys_idx := chain.item(cache_keys_idx);
  629.                end;
  630.             else
  631.                from
  632.                   i := buckets.item(cache_buckets_idx);
  633.                until
  634.                   chain.item(i) = cache_keys_idx
  635.                loop
  636.                   i := chain.item(i);
  637.                end;
  638.                cache_keys_idx := i;
  639.             end;
  640.          elseif idx = cache_user_idx then
  641.          elseif idx = 1 then
  642.             cache_user_idx := 1;
  643.             from
  644.                cache_buckets_idx := 0;
  645.             until
  646.                buckets.item(cache_buckets_idx) >= 0
  647.             loop
  648.                cache_buckets_idx := cache_buckets_idx + 1;
  649.             end;
  650.             cache_keys_idx := buckets.item(cache_buckets_idx);
  651.          elseif idx = count then
  652.             cache_user_idx := idx;
  653.             from
  654.                cache_buckets_idx := buckets.upper;
  655.             until
  656.                buckets.item(cache_buckets_idx) >= 0
  657.             loop
  658.                cache_buckets_idx := cache_buckets_idx - 1;
  659.             end;
  660.             from
  661.                cache_keys_idx := buckets.item(cache_buckets_idx);
  662.             until
  663.                chain.item(cache_keys_idx) < 0
  664.             loop
  665.                cache_keys_idx := chain.item(cache_keys_idx);
  666.             end;
  667.          else
  668.             from
  669.            cache_user_idx := 1;
  670.            from
  671.           cache_buckets_idx := 0;
  672.            until
  673.           buckets.item(cache_buckets_idx) >= 0
  674.            loop
  675.           cache_buckets_idx := cache_buckets_idx + 1;
  676.            end;
  677.            cache_keys_idx := buckets.item(cache_buckets_idx);
  678.             until
  679.                cache_user_idx = idx
  680.             loop
  681.                set_cache_user_idx(cache_user_idx + 1);
  682.             end;
  683.          end;
  684.       ensure
  685.          cache_user_idx = idx;
  686.          buckets.valid_index(cache_buckets_idx);
  687.          keys.valid_index(cache_keys_idx);
  688.       end;
  689.    
  690. invariant
  691.  
  692.    (keys.upper = store.upper) and (store.upper = chain.upper);
  693.  
  694.    buckets.upper = modulus - 1;
  695.  
  696.    -1 <= first_free_slot and first_free_slot <= chain.upper;
  697.    
  698. end -- DICTIONARY[V,K->HASHABLE]
  699.  
  700.